////////////////////////////////////////////////////////////////////////////
//  Copyright 2009 - 2011 Zach Snow                                       //
////////////////////////////////////////////////////////////////////////////
//  This file is part of WindowsWhere.                                    //
//                                                                        //
//  WindowsWhere is free software: you can redistribute it and/or modify  //
//  it under the terms of the GNU General Public License as published by  //
//  the Free Software Foundation, either version 3 of the License, or     //
//  (at your option) any later version.                                   //
//                                                                        //
//  WindowsWhere is distributed in the hope that it will be useful,       //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of        //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         //
//  GNU General Public License for more details.                          //
//                                                                        //
//  You should have received a copy of the GNU General Public License     //
//  along with WindowsWhere.  If not, see <http://www.gnu.org/licenses/>. //
////////////////////////////////////////////////////////////////////////////
// This file was derived from GlobalCbtHook.cpp, by Chris Wilson          //
////////////////////////////////////////////////////////////////////////////
#include "GlobalHook.h"

HHOOK hookCbt = NULL;
HHOOK hookShell = NULL;
HHOOK hookKeyboard = NULL;
HHOOK hookMouse = NULL;
HHOOK hookKeyboardLL = NULL;
HHOOK hookMouseLL = NULL;
HHOOK hookCallWndProc = NULL;
HHOOK hookGetMsg = NULL;

// Store the application instance of this module to pass to
// hook initialization. This is set in DllMain().
HINSTANCE g_Instance = NULL;

typedef void (CALLBACK *HookProc)(int code, WPARAM w, LPARAM l);

static LRESULT CALLBACK CbtHookCallback(int code, WPARAM wparam, LPARAM lparam);
static LRESULT CALLBACK ShellHookCallback(int code, WPARAM wparam, LPARAM lparam);
static LRESULT CALLBACK KeyboardHookCallback(int code, WPARAM wparam, LPARAM lparam);
static LRESULT CALLBACK MouseHookCallback(int code, WPARAM wparam, LPARAM lparam);
static LRESULT CALLBACK KeyboardLLHookCallback(int code, WPARAM wparam, LPARAM lparam);
static LRESULT CALLBACK MouseLLHookCallback(int code, WPARAM wparam, LPARAM lparam);
static LRESULT CALLBACK CallWndProcHookCallback(int code, WPARAM wparam, LPARAM lparam);
static LRESULT CALLBACK GetMsgHookCallback(int code, WPARAM wparam, LPARAM lparam);

bool InitializeCbtHook(int threadID, HWND destination)
{
  if (g_Instance == NULL)
  {
    return false;
  }

  if (GetProp(GetDesktopWindow(), "WW_HOOK_HWND_CBT") != NULL)
  {
    SendNotifyMessage((HWND)GetProp(GetDesktopWindow(), "WW_HOOK_HWND_CBT"), RegisterWindowMessage("WW_HOOK_CBT_REPLACED"), 0, 0);
  }

  SetProp(GetDesktopWindow(), "WW_HOOK_HWND_CBT", destination);

  hookCbt = SetWindowsHookEx(WH_CBT, (HOOKPROC)CbtHookCallback, g_Instance, threadID);
  return hookCbt != NULL;
}

void UninitializeCbtHook()
{
  if (hookCbt != NULL)
    UnhookWindowsHookEx(hookCbt);
  hookCbt = NULL;
}

static LRESULT CALLBACK CbtHookCallback(int code, WPARAM wparam, LPARAM lparam)
{
  if (code >= 0)
  {
    UINT msg = 0;

    if (code == HCBT_ACTIVATE)
      msg = RegisterWindowMessage("WW_HOOK_HCBT_ACTIVATE");
    else if (code == HCBT_CREATEWND)
      msg = RegisterWindowMessage("WW_HOOK_HCBT_CREATEWND");
    else if (code == HCBT_DESTROYWND)
      msg = RegisterWindowMessage("WW_HOOK_HCBT_DESTROYWND");
    else if (code == HCBT_MINMAX)
      msg = RegisterWindowMessage("WW_HOOK_HCBT_MINMAX");
    else if (code == HCBT_MOVESIZE)
      msg = RegisterWindowMessage("WW_HOOK_HCBT_MOVESIZE");
    else if (code == HCBT_SETFOCUS)
      msg = RegisterWindowMessage("WW_HOOK_HCBT_SETFOCUS");
    else if (code == HCBT_SYSCOMMAND)
      msg = RegisterWindowMessage("WW_HOOK_HCBT_SYSCOMMAND");

    HWND dstWnd = (HWND)GetProp(GetDesktopWindow(), "WW_HOOK_HWND_CBT");

    if (msg != 0)
      SendNotifyMessage(dstWnd, msg, wparam, lparam);
  }

  return CallNextHookEx(hookCbt, code, wparam, lparam);
}

bool InitializeShellHook(int threadID, HWND destination)
{
  if (g_Instance == NULL)
  {
    return false;
  }

  if (GetProp(GetDesktopWindow(), "WW_HOOK_HWND_SHELL") != NULL)
  {
    SendNotifyMessage((HWND)GetProp(GetDesktopWindow(), "WW_HOOK_HWND_SHELL"), RegisterWindowMessage("WW_HOOK_SHELL_REPLACED"), 0, 0);
  }

  SetProp(GetDesktopWindow(), "WW_HOOK_HWND_SHELL", destination);

  hookShell = SetWindowsHookEx(WH_SHELL, (HOOKPROC)ShellHookCallback, g_Instance, threadID);
  return hookShell != NULL;
}

void UninitializeShellHook()
{
  if (hookShell != NULL)
    UnhookWindowsHookEx(hookShell);
  hookShell = NULL;
}

static LRESULT CALLBACK ShellHookCallback(int code, WPARAM wparam, LPARAM lparam)
{
  if (code >= 0)
  {
    UINT msg = 0;

    if (code == HSHELL_ACTIVATESHELLWINDOW)
      msg = RegisterWindowMessage("WW_HOOK_HSHELL_ACTIVATESHELLWINDOW");
    else if (code == HSHELL_GETMINRECT)
      msg = RegisterWindowMessage("WW_HOOK_HSHELL_GETMINRECT");
    else if (code == HSHELL_LANGUAGE)
      msg = RegisterWindowMessage("WW_HOOK_HSHELL_LANGUAGE");
    else if (code == HSHELL_REDRAW)
      msg = RegisterWindowMessage("WW_HOOK_HSHELL_REDRAW");
    else if (code == HSHELL_TASKMAN)
      msg = RegisterWindowMessage("WW_HOOK_HSHELL_TASKMAN");
    else if (code == HSHELL_WINDOWACTIVATED)
      msg = RegisterWindowMessage("WW_HOOK_HSHELL_WINDOWACTIVATED");
    else if (code == HSHELL_WINDOWCREATED)
      msg = RegisterWindowMessage("WW_HOOK_HSHELL_WINDOWCREATED");
    else if (code == HSHELL_WINDOWDESTROYED)
      msg = RegisterWindowMessage("WW_HOOK_HSHELL_WINDOWDESTROYED");

    HWND dstWnd = (HWND)GetProp(GetDesktopWindow(), "WW_HOOK_HWND_SHELL");

    if (msg != 0)
      SendNotifyMessage(dstWnd, msg, wparam, lparam);
  }

  return CallNextHookEx(hookShell, code, wparam, lparam);
}

bool InitializeKeyboardHook(int threadID, HWND destination)
{
  if (g_Instance == NULL)
  {
    return false;
  }

  if (GetProp(GetDesktopWindow(), "WW_HOOK_HWND_KEYBOARD") != NULL)
  {
    SendNotifyMessage((HWND)GetProp(GetDesktopWindow(), "WW_HOOK_HWND_KEYBOARD"), RegisterWindowMessage("WW_HOOK_KEYBOARD_REPLACED"), 0, 0);
  }

  SetProp(GetDesktopWindow(), "WW_HOOK_HWND_KEYBOARD", destination);

  hookKeyboard = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)KeyboardHookCallback, g_Instance, threadID);
  return hookKeyboard != NULL;
}

void UninitializeKeyboardHook()
{
  if (hookKeyboard != NULL)
    UnhookWindowsHookEx(hookKeyboard);
  hookKeyboard = NULL;
}

static LRESULT CALLBACK KeyboardHookCallback(int code, WPARAM wparam, LPARAM lparam)
{
  if (code >= 0)
  {
    UINT msg = 0;

    msg = RegisterWindowMessage("WW_HOOK_KEYBOARD");

    HWND dstWnd = (HWND)GetProp(GetDesktopWindow(), "WW_HOOK_HWND_KEYBOARD");

    if (msg != 0)
      SendNotifyMessage(dstWnd, msg, wparam, lparam);
  }

  return CallNextHookEx(hookKeyboard, code, wparam, lparam);
}

bool InitializeMouseHook(int threadID, HWND destination)
{
  if (g_Instance == NULL)
  {
    return false;
  }

  if (GetProp(GetDesktopWindow(), "WW_HOOK_HWND_MOUSE") != NULL)
  {
    SendNotifyMessage((HWND)GetProp(GetDesktopWindow(), "WW_HOOK_HWND_MOUSE"), RegisterWindowMessage("WW_HOOK_MOUSE_REPLACED"), 0, 0);
  }

  SetProp(GetDesktopWindow(), "WW_HOOK_HWND_MOUSE", destination);

  hookMouse = SetWindowsHookEx(WH_MOUSE, (HOOKPROC)MouseHookCallback, g_Instance, threadID);
  return hookMouse != NULL;
}

void UninitializeMouseHook()
{
  if (hookMouse != NULL)
    UnhookWindowsHookEx(hookMouse);
  hookMouse = NULL;
}

static LRESULT CALLBACK MouseHookCallback(int code, WPARAM wparam, LPARAM lparam)
{
  if (code >= 0)
  {
    UINT msg = 0;

    msg = RegisterWindowMessage("WW_HOOK_MOUSE");

    HWND dstWnd = (HWND)GetProp(GetDesktopWindow(), "WW_HOOK_HWND_MOUSE");

    if (msg != 0)
      SendNotifyMessage(dstWnd, msg, wparam, lparam);
  }

  return CallNextHookEx(hookMouse, code, wparam, lparam);
}

bool InitializeKeyboardLLHook(int threadID, HWND destination)
{
  if (g_Instance == NULL)
  {
    return false;
  }

  if (GetProp(GetDesktopWindow(), "WW_HOOK_HWND_KEYBOARDLL") != NULL)
  {
    SendNotifyMessage((HWND)GetProp(GetDesktopWindow(), "WW_HOOK_HWND_KEYBOARDLL"), RegisterWindowMessage("WW_HOOK_KEYBOARDLL_REPLACED"), 0, 0);
  }

  SetProp(GetDesktopWindow(), "WW_HOOK_HWND_KEYBOARDLL", destination);

  hookKeyboardLL = SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)KeyboardLLHookCallback, g_Instance, threadID);
  return hookKeyboardLL != NULL;
}

void UninitializeKeyboardLLHook()
{
  if (hookKeyboardLL != NULL)
    UnhookWindowsHookEx(hookKeyboardLL);
  hookKeyboardLL = NULL;
}

static LRESULT CALLBACK KeyboardLLHookCallback(int code, WPARAM wparam, LPARAM lparam)
{
  if (code >= 0)
  {
    UINT msg = 0;

    msg = RegisterWindowMessage("WW_HOOK_KEYBOARDLL");

    HWND dstWnd = (HWND)GetProp(GetDesktopWindow(), "WW_HOOK_HWND_KEYBOARDLL");

    if (msg != 0)
      SendNotifyMessage(dstWnd, msg, wparam, lparam);
  }

  return CallNextHookEx(hookKeyboardLL, code, wparam, lparam);
}

bool InitializeMouseLLHook(int threadID, HWND destination)
{
  if (g_Instance == NULL)
  {
    return false;
  }

  if (GetProp(GetDesktopWindow(), "WW_HOOK_HWND_MOUSELL") != NULL)
  {
    SendNotifyMessage((HWND)GetProp(GetDesktopWindow(), "WW_HOOK_HWND_MOUSELL"), RegisterWindowMessage("WW_HOOK_MOUSELL_REPLACED"), 0, 0);
  }

  SetProp(GetDesktopWindow(), "WW_HOOK_HWND_MOUSELL", destination);

  hookMouseLL = SetWindowsHookEx(WH_MOUSE_LL, (HOOKPROC)MouseLLHookCallback, g_Instance, threadID);
  return hookMouseLL != NULL;
}

void UninitializeMouseLLHook()
{
  if (hookMouseLL != NULL)
    UnhookWindowsHookEx(hookMouseLL);
  hookMouseLL = NULL;
}

static LRESULT CALLBACK MouseLLHookCallback(int code, WPARAM wparam, LPARAM lparam)
{
  if (code >= 0)
  {
    UINT msg = 0;

    msg = RegisterWindowMessage("WW_HOOK_MOUSELL");

    HWND dstWnd = (HWND)GetProp(GetDesktopWindow(), "WW_HOOK_HWND_MOUSELL");

    if (msg != 0)
      SendNotifyMessage(dstWnd, msg, wparam, lparam);
  }

  return CallNextHookEx(hookMouseLL, code, wparam, lparam);
}

bool InitializeCallWndProcHook(int threadID, HWND destination)
{
  if (g_Instance == NULL)
  {
    return false;
  }

  if (GetProp(GetDesktopWindow(), "WW_HOOK_HWND_CALLWNDPROC") != NULL)
  {
    SendNotifyMessage((HWND)GetProp(GetDesktopWindow(), "WW_HOOK_HWND_CALLWNDPROC"),
      RegisterWindowMessage("WW_HOOK_CALLWNDPROC_REPLACED"), 0, 0);
  }

  SetProp(GetDesktopWindow(), "WW_HOOK_HWND_CALLWNDPROC", destination);

  hookCallWndProc = SetWindowsHookEx(WH_CALLWNDPROC, (HOOKPROC)CallWndProcHookCallback, g_Instance, threadID);
  return hookCallWndProc != NULL;
}

void UninitializeCallWndProcHook()
{
  if (hookCallWndProc != NULL)
    UnhookWindowsHookEx(hookCallWndProc);
  hookCallWndProc = NULL;
}

static LRESULT CALLBACK CallWndProcHookCallback(int code, WPARAM wparam, LPARAM lparam)
{
  if (code >= 0)
  {
    UINT msg = 0;
    UINT msg2 = 0;

    msg = RegisterWindowMessage("WW_HOOK_CALLWNDPROC");
    msg2 = RegisterWindowMessage("WW_HOOK_CALLWNDPROC_PARAMS");

    HWND dstWnd = (HWND)GetProp(GetDesktopWindow(), "WW_HOOK_HWND_CALLWNDPROC");
    
    CWPSTRUCT* pCwpStruct = (CWPSTRUCT*)lparam;

    if (msg != 0 && pCwpStruct->message != msg && pCwpStruct->message != msg2)
    {
      SendNotifyMessage(dstWnd, msg, (WPARAM)pCwpStruct->hwnd, pCwpStruct->message);
      SendNotifyMessage(dstWnd, msg2, pCwpStruct->wParam, pCwpStruct->lParam);
    }
  }

  return CallNextHookEx(hookCallWndProc, code, wparam, lparam);
}

bool InitializeGetMsgHook(int threadID, HWND destination)
{
  if (g_Instance == NULL)
  {
    return false;
  }

  if (GetProp(GetDesktopWindow(), "WW_HOOK_HWND_GETMSG") != NULL)
  {
    SendNotifyMessage((HWND)GetProp(GetDesktopWindow(), "WW_HOOK_HWND_GETMSG"), RegisterWindowMessage("WW_HOOK_GETMSG_REPLACED"), 0, 0);
  }

  SetProp(GetDesktopWindow(), "WW_HOOK_HWND_GETMSG", destination);

  hookGetMsg = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgHookCallback, g_Instance, threadID);
  return hookGetMsg != NULL;
}

void UninitializeGetMsgHook()
{
  if (hookGetMsg != NULL)
    UnhookWindowsHookEx(hookGetMsg);
  hookGetMsg = NULL;
}

static LRESULT CALLBACK GetMsgHookCallback(int code, WPARAM wparam, LPARAM lparam)
{
  if (code >= 0)
  {
    UINT msg = 0;
    UINT msg2 = 0;

    msg = RegisterWindowMessage("WW_HOOK_GETMSG");
    msg2 = RegisterWindowMessage("WW_HOOK_GETMSG_PARAMS");

    HWND dstWnd = (HWND)GetProp(GetDesktopWindow(), "WW_HOOK_HWND_GETMSG");
    
    MSG* pMsg = (MSG*)lparam;

    if (msg != 0 && pMsg->message != msg && pMsg->message != msg2)
    {
      SendNotifyMessage(dstWnd, msg, (WPARAM)pMsg->hwnd, pMsg->message);
      SendNotifyMessage(dstWnd, msg2, pMsg->wParam, pMsg->lParam);
    }
  }

  return CallNextHookEx(hookGetMsg, code, wparam, lparam);
}
